/**
 * @file tasks.h
 * @author Linquan 
 * @brief 
 * @version 0.1
 * @date 2024-12-08
 * 
 * @copyright Copyright (c) 2024
 * 
 */

#ifndef TASKS_H
#define TASKS_H

/**> tasks 运行依赖： */
#include "list.h"
#include "nodebuf.h"

#define USE_TIMER           1 /**> 使用32定时器进行任务时间调度 */ 

#if USE_TIMER

/**> 任务运行间隔时间宏 @task_delay_time */
#ifndef S
#define S_(X)    X*1000*1000 /* s */   
#endif

#ifndef MS
#define MS_(X)    X*1000 /* ms */   
#endif

#ifndef US
#define US_(X)    X /* us */   
#endif
 

/**> 定时器中断时间宏 @macro_Timer_interrupt_time */ 
#ifndef TASK_TICK_MS
#define TASK_TICK_MS(X)    X*1000 /* ms */   
#endif

#ifndef TASK_TICK_US
#define TASK_TICK_US(X)    X /* us */   
#endif

#endif

/* 任务调度 计时类型 */
#define TIME_TYPE_64   uint64_t
#define TIME_TYPE_32   uint32_t

/* 选择计时器的类型 */
#define TIME_TYPE       TIME_TYPE_64

/* 时间累计溢出阈值 */
#if (TIME_TYPE == TIME_TYPE_64)
    //阈值为2^63-1，用于区分“已超时”和“未到时间”（含溢出情况）
    #define TIME_OVER_MAX   0x7FFFFFFFFFFFFFFFULL
#elif
    #define TIME_OVER_MAX   0x7FFFFFFF
#endif

/* 任务传入参数 宏封装：自动绑定参数类型和值（用户无需手动处理类型） */
#define TASK_PARAM_NONE()  ((task_param_t){.type = TASK_PARAM_NONE})
#define TASK_PARAM_INT(val)  ((task_param_t){.type = TASK_PARAM_INT, .data.i_val = (val)})
#define TASK_PARAM_FLOAT(val)((task_param_t){.type = TASK_PARAM_FLOAT, .data.f_val = (val)})
#define TASK_PARAM_PTR(val)  ((task_param_t){.type = TASK_PARAM_PTR, .data.p_val = (val)})
#define TASK_PARAM_OBJ(val)  ((task_param_t){.type = TASK_PARAM_OBJ, .data.p_val = (val)})

/* 任务传入参数 安全取值宏：自动校验类型，避免错误转换（调试时可加断言） */ 
#define CHECK_PARAM_NONE(param)  \
    do { \
        if ((param)->type != TASK_PARAM_NONE) { \
            abort(); \
        } \
    } while(0)

#define GET_PARAM_INT(param)  \
    ( (param)->type == TASK_PARAM_INT ? (param)->data.i_val : (abort(), 0) )

#define GET_PARAM_FLOAT(param)  \
    ( (param)->type == TASK_PARAM_FLOAT ? (param)->data.f_val : (abort(), 0.0f) )

#define GET_PARAM_PTR(param)  \
    ( (param)->type == TASK_PARAM_PTR ? (param)->data.p_val : (abort(), NULL) )

#define GET_PARAM_OBJ(param, obj_type)  \
    ( (param)->type == TASK_PARAM_OBJ ? (obj_type*)(param)->data.p_val : (abort(), NULL) )


/**>
 * 任务(task)和定时器(timer)管理系统。
 * 
 * 任务有延迟启动属性，支持在未来某个时间点执行；
 * 如果未设定延迟启动，该任务将被尽快调度执行。
 * 通过任务处理函数`task_fn`的返回值r，可以设定其r毫秒之后再次执行（实现timer机制）。
 * 任务处理函数返回0表示任务已经终结，不会被再次执行。
 * 
 * 此任务管理器要求使用者不断地调用`tasks_schedule()`以便驱动各任务按预期行为执行。
 * by liigo 20200218, 20201030.
 * 
 */
struct tasks_t {
    struct nodebuf_t taskbuf;
    uint8_t needfree;
    TIME_TYPE tick_time;    // 任务的节拍的时间长度 
    TIME_TYPE tick_count;   // 该任务链表的时钟节拍计数器
    struct list_head tasks; // 在结构体内声明表头，在函数内部初始化
};

/**>
 * 任务处理函数的参数，参见`task_fn`
 * 支持传入一个int类型、浮点数类型、指针类型或自定义数据类型
 */
// 1. 定义参数类型标记（支持基础类型和自定义类型）
typedef enum {
    TASK_PARAM_NONE,      // 无参数（新增）
    TASK_PARAM_INT,       // 整数类型
    TASK_PARAM_FLOAT,     // 浮点类型
    TASK_PARAM_PTR,       // 指针类型（如字符串、数组）
    TASK_PARAM_OBJ        // 自定义结构体/对象（复杂类型）
} task_param_type_t;

// 2. 通用参数容器：支持基础类型和自定义类型
typedef struct 
{
    task_param_type_t type;  // 类型标记（用于安全校验）
    union 
    {
        int     i_val;         // 存储int类型
        float   f_val;         // 存储float类型
        void*   p_val;         // 存储指针/自定义类型（复用）
    } data;                    // 数据存储（联合体节省空间）
} task_param_t;

/**>
 * 任务处理函数，用在执行此任务内各项工作。
 * 如果返回值n>0，表示此任务将在（n/时基）个tick后再次执行。
 * 如果返回值n=0，表示此任务已终结，不会再次执行。
 */
typedef int task_fn(task_param_t* args);

/**>
 * 任务节点：
 */
struct task_node_t 
{
    struct list_head    list;       // 用作把任务串联为双向链表，要求是首成员
    task_fn *           fn;         // 返回值n大于0表示n毫秒后再次执行此任务（详见`task_fn`）
    task_param_t        param;      // 任务执行参数，执行时传入task_fn
    TIME_TYPE            exe_time;   // 预定执行时间点，以毫秒为单位的时间戳，可为0
};

/**
 * @brief 初始化任务管理器
 * 内部预分配内存空间(malloc)，可存储约ntask个任务；预分配空间用完后按需逐任务申请内存(malloc)。
 * task_tick_ms 设置任务的节拍的时间长度，单位ms
 * 
 * @param tasks 
 * @param ntask 
 * @param task_tick_ms 
 */
void tasks_init(struct tasks_t *tasks, int ntask, unsigned int task_tick_ms ) ;

/**
 * @brief  初始化任务管理器
 * 内部使用由调用者提供的预分配内存空间；预分配空间用完后按需逐任务申请内存(malloc)。
 * task_tick_ms 设置任务的节拍的时间长度，单位ms
 * 
 * @param tasks 
 * @param buf 
 * @param buflen 
 * @param task_tick_ms 
 */
void tasks_init_withbuf(struct tasks_t *tasks, void *buf, int buflen, unsigned int task_tick_ms );


/**
 * @brief 析构任务管理器
 * 
 * @param tasks 
 */
void task_fini(struct tasks_t *tasks);

/**
 * @brief 往任务管理器中添加一个任务，任务由函数fn及其参数指定
 *  delay_time > 0`表示延迟指定时钟节拍后执行
 *  如无延迟，任务将按照加入顺序被执行，即先加入先执行、后加入后执行。
 * 
 * @param tasks 
 * @param delay_ms 
 * @param fn 
 * @param args
 */
void tasks_add_task(struct tasks_t *tasks, 
    int delay_time, task_fn *fn, task_param_t args);


/**
 * @brief  从任务管理器中删除任务，通过比较任务函数指针进行匹配
 * 如果参数once非0，删除最早加入的第一个匹配任务；如果once为0，删除所有匹配任务；
 * 返回被删除的任务个数。
 * 
 * @param tasks 
 * @param fn 
 * @param once 
 * @return int 
 */
int tasks_remove_task(struct tasks_t *tasks, task_fn *fn, int once);


/**
 * @brief 调度任务管理器驱动各任务按预期行为执行
 *  使用者应当足够频繁的循环调用此函数，以确保任务管理器正常运作
 * 
 * @param tasks 
 */
void tasks_schedule(struct tasks_t *tasks);

// 往控制台窗口输出当前所有任务及其属性
void tasks_print_all(struct tasks_t *tasks, int64_t now_ms, const char *name);


/* 当不使用定时器计时时，下述两个函数就不起作用 */
#if USE_TIMER
/**
 * @brief 任务运行时间获取,在内部使用 GET_NOW_TIME宏 调用
 * 通过使用 定时器 时钟节拍调用的方法调度任务集
 * 返回任务当前运行时间：节拍数×时钟片长度
 *  
 * @param tasks 
 * @param op 对时钟计数器的操作：读取、累加。
 * @return TIME_TYPE 
 */
TIME_TYPE get_now_time(struct tasks_t *tasks);


/**
 * @brief 任务集 时钟节拍累加
 * 使用 定时器 时钟节拍调用的方法调度任务集时使用
 * 将该函数 放在 定时器中断中，对定时器中断时间进行累加
 * 
 * @param tasks 
 */
void task_time_add(struct tasks_t *tasks);

#endif

/**
 * @brief 协程，状态机调度
 * 用于 单线程 非阻塞式多任务处理。 
 * 
 * excample:
 * void CO_test()
 * {
 *      CO_INIT(wait_reply);
 *      CO_START(wait_reply);
 *      while(1)
 *      {
 *          // 运行 用户代码
 *          ...
 *          // 阻塞等待从机回复，切换去处理其他任务
 *          CO_WAIT_WHILE(wait_reply, 满足的条件)；
 *  
 *          // 收到从机回复或回复超时，继续向下执行
 *          ...
 *          // 执行完一次，切换出去处理其他任务
 *          CO_YIELD(wait_reply)
 *      }
 *      // 执行结束
 *      CO_END(wait_reply)；
 * }
 * 
 */

// 初始化协程状态变量（静态uint32_t，保存执行位置标记）
#define CO_INIT(s)  static uint32_t s = 0;

// 启动/恢复协程执行（从上次保存的状态继续，对应switch入口）
#define CO_START(s) switch(s) { case 0:

// 保存当前执行状态（记录行号作为标记，下次从这里恢复）
#define CO_YIELD(s) s = __LINE__; case __LINE__:

// 结束协程状态块（重置状态并闭合switch结构）
#define CO_END(s)   s = 0; }

// 等待直到条件满足（不满足则保存状态并退出，下次恢复再判断）
#define CO_WAIT_UNTIL(co, condition)        \
  do {                                      \
    CO_YIELD(co);                           \
    if (!(condition)) {                     \
      return;                               \
    }                                       \
  } while(0)

// 等待条件为真时暂停（条件为真则保存状态退出，直到条件为假再继续）
#define CO_WAIT_WHILE(co, cond)  CO_WAIT_UNTIL((co), !(cond))

// 重启协程（重置状态为初始值，下次执行从开头重新开始）
#define CO_RESTART(s)  do { (s) = 0; return; } while(0)








#ifdef __cplusplus
} // extern "C"
#endif

#endif // TASKS_H
